package com.netty.server;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author
 */
@Component
public class NettyServer {
    /**
     * boss事件轮询线程组
     * 处理Accept连接事件的线程，这里线程数设置为1即可，netty处理链接事件默认为单线程，过度设置反而浪费cpu资源
     */
    private EventLoopGroup boss = new NioEventLoopGroup(1);

    /**
     * worker事件轮询线程组
     * 处理hadnler的工作线程，其实也就是处理IO读写 。线程数据默认为 CPU 核心数乘以2
     */
    private EventLoopGroup worker = new NioEventLoopGroup();

    /**
     * 存储client的channel
     * key:ip，value:Channel
     */
    public static Map<String, Channel> map = new ConcurrentHashMap<String, Channel>();

    @Autowired
    private ServerChannelInitializer serverChannelInitializer;

    @Value("${netty.server.port}")
    private Integer port;

    /**与客户端建立连接后得到的通道对象*/
    private Channel channel;
    /**
     * 设置服务端端口
     * @throws Exception
     */
    public  ChannelFuture start()  {
        ServerBootstrap bootstrap = new ServerBootstrap();
        //第1步定义两个线程组，用来处理客户端通道的accept和读写事件
        bootstrap.group(boss,worker)
                //第2步绑定服务端通道
                .channel(NioServerSocketChannel.class)
                //用于构造服务端套接字ServerSocket对象，标识当服务器请求处理线程全满时，用于临时存放已完成三次握手的请求的队列的最大长度。
                //用来初始化服务端可连接队列
                //服务端处理客户端连接请求是按顺序处理的，所以同一时间只能处理一个客户端连接，多个客户端来的时候，服务端将不能处理的客户端连接请求放在队列中等待处理，backlog参数指定了队列的大小。
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //第3步绑定handler，处理读写事件，ChannelInitializer是给通道初始化
                .childHandler(serverChannelInitializer);
        // 绑定端口，同步等待成功
        ChannelFuture channelFuture1 = bootstrap.bind(port).syncUninterruptibly();
        if (channelFuture1 != null && channelFuture1.isSuccess()) {
            //获取通道
            channel = channelFuture1.channel();
            System.out.println("Netty 服务 启动 成功, port ="+ port);
        } else {
            System.out.println("Netty 服务 启动 失败");
        }
        return channelFuture1;
    }


    /**
     * 停止Netty tcp server服务
     */
    @PreDestroy
    public void destroy() {
        System.out.println("==========Netty服务退出，释放线程资源==========");
        if (channel != null) {
            channel.close();
        }
        try {
            Future<?> future = worker.shutdownGracefully().await();
            if (!future.isSuccess()) {
                System.out.println("netty tcp workerGroup shutdown fail"+ future.cause());
            }
            Future<?> future1 = boss.shutdownGracefully().await();
            if (!future1.isSuccess()) {
                System.out.println("netty tcp bossGroup shutdown fail {}"+future1.cause());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Netty服务关闭成功");
    }

}
